package guru.merlin.server;

import guru.merlin.bus.EnvelopeBus;
import guru.merlin.core.PeerEPException;
import guru.merlin.core.EnvelopeProcessor;
import guru.merlin.core.Route;
import guru.merlin.core.Router;
import guru.util.Commandable;
import guru.util.Commander;
import guru.util.ObjectManager;
import guru.util.SMTPEngine;

import java.io.FileInputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

/**
 * @author cobach Clase EPServer, antigua clase Agent, modificada por santiago ventura el
 *         22/07/2005. Init Level 1: bus cerrado, sin nodos Init Level 2: bus cerrado, nodos
 *         montados sin correr corriendo Init Level 3: bus cerrado, nodos montados y corriendo Init
 *         level 4: bus abierto, nodos montados y corriendo.
 */
public class EPServer implements Commandable{
    static Logger logger = Logger.getLogger(EPServer.class);
    
    protected EnvelopeBus bus;
    private Nodes nodes;
    private Commander commander;
    private Router router;
    //private Map preRouters;
    private String homePath;
    private String agentName;
    
    /**
     * @return
     */
    public static Properties defaultLoggerProperties() {
        Properties properties = new Properties();
        properties.put("log4j.rootCategory", "debug,console");
        properties.put("log4j.appender.console", "org.apache.log4j.ConsoleAppender");
        properties.put("log4j.appender.console.layout", "org.apache.log4j.PatternLayout");
        properties.put("log4j.appender.console.layout.ConversionPattern", "[%d{dd MM yyyy HH:mm:ss,SSS}]%-5p(%t): %m [%c]%n");
        return properties;
    }
    
    public static void main(String argv[]) {
        
        if (argv.length != 1) {
            System.out.println("syntax: java guru.merlin.core.Agent <Initialization File Path>");
        } else {
            try {
                new EPServer(argv[0]);
            } catch (Throwable t) {
                System.out.println("-------------------------------------------------");
                System.out.println("            A FATAL ERROR HAS OCURRED");
                System.out.println("            	Agent stoped");
                System.out.println("-------------------------------------------------");
                t.printStackTrace();
                logger.fatal("Agent Stoped", t);
                System.exit(-1);
                
            }
        }
    }
    
    /**
     * @param initFilePath
     * @throws Exception
     */
    protected EPServer(String initFilePath) throws Exception {
        Thread.currentThread().setName("Merlin");
        
        System.out.println("-------------------------------------------------");
        System.out.println("Merlin (Message Rely Infrastructure)");
        //System.out.println("Agent ver 2.0");
        System.out.println("EP Server ver 2.2.0");
        System.out.println("-------------------------------------------------");
        
        System.out.println("Copyright Cesar Obach-Renner, 'cobach@guru.com.ve'");
        System.out.println("All rights reserved.");
        System.out.println("-------------------------------------------------");
        
        /** ************************************************** */
        commander = new Commander(initFilePath, "agent.name;router.class;agent.home;router.routingTable", this);
        
        PropertyConfigurator.configure(initFilePath);
        
        agentName = commander.getValue("agent.name");
        String routerStr = commander.getValue("router.class");
        String routingTableFilePath = commander.getValue("router.routingTable");
        homePath = commander.getValue("agent.home");
        
        logger.info("-------------------------------------------------");
        logger.info("Agent Name: " + agentName);
        logger.info("-------------------------------------------------");
        
        /** ************************************************** */
        
        logger.info("HOME:" + homePath);
        
        bus = new EnvelopeBus();
        //DEBE SER MODIFICADO PARA QUE SEA UN MAP DE CONTAINERS
        nodes = new Nodes();
        
        Object[] args = { bus, commander, routingTableFilePath };
        router = (Router) ObjectManager.create(routerStr, args);
        
        if (router == null) logger.warn("router@startup is null");
        else logger.info("Router version: " + router.getVersion());
        
        //preRouters = new HashMap();
        
        logger.info("Agent up and running.");
        
        Properties props=new Properties();
        props.load(new FileInputStream(initFilePath));
        SMTPEngine.smtp_host = props.getProperty("mail.smtp.host");
        
        commander.start();
        System.gc();
    }
    
    public void initCommands() {
        /*
         * mount("email.StubEMailHandler","email1"); start("email1");
         */
        
        System.out.println("-------------------------------------------------");
        
    }
    
    /***********************************************************************************************
     * Manipuladores de MsgProcessors **
     * ********************************************************************************************************
     * ********************************************************************************************************
     **********************************************************************************************/
    
    public String openBus() {
        if (nodes.size() > 0) {
            try {
                bus.start();
            } catch (Exception e) {
                return ("Cannot open Bus for incoming messages: " + e);
            }
            return ("Bus opened for incoming messages.");
        } else {
            return ("There is no gateway mounted");
        }
    }
    
    public String closeBus() {
        if (nodes.size() > 0) {
            try {
                bus.stop();
            } catch (Exception e) {
                return ("Cannot close Bus for incoming messages: " + e);
            }
            return ("Bus closed for incoming messages.");
        } else {
            return ("There is no gateway mounted");
        }
    }
    
    public String init1() {
        return ("command not implemented");
    }
    
    /**
     * @param className
     *            Nombre de la clase sin el ".class"
     */
    public String shutdown() throws Exception {
        logger.debug("Shuting down '" + agentName + "'");
        
        nodes.stopAll();
        commander.finalize();
        
        try {
            bus.finalize();
        } catch (Exception e) {
            //revisar
        }
        
        Thread.yield();
        
        logger.info("Server shuted down");
        return ("done");
    }
    
    /*
     * public String setMode(String mode){ return(Log.println(1,"Server set in '"+mode+"' mode.")); }
     */
    
    /*
     * Modificado por santiago ventura. Originalmente la firma del mtodo era la siguiete: public
     * String mount(String alias, String className, String propertiesFilePath, String route) Se
     * elimino el argumento route del mtodo.
     *
     */
    public String mount(String alias, String className, String propertiesFilePath) {
        if (router == null) logger.debug("router@mount is null");
        else logger.debug("router@mount is NOT null");
        
        EnvelopeProcessor ep;
        Route route;
        logger.debug("Trying to mount '" + className + "' as '" + alias + "' with propertiesFilePath '" + propertiesFilePath + "' on route '" + alias + "'");
        try {
            
            ep = (EnvelopeProcessor) ObjectManager.create(className);
            PreRouter preRouter = new PreRouter(alias, router);
            ep.addEnvelopeHandler(preRouter);
            //Modificado por Santiago 26092005
            //EPContainer epContainer=new EPContainer(ep,router,bus);
            
            int maxAttemptsReconect = Integer.parseInt(commander.getValue("max_attempts_reconect", "10"));
            //long macroCycle = Long.parseLong(commander.getValue("macrocycle","1800000"));
            long microCycle = Long.parseLong(commander.getValue("microcycle", "60000"));
            int monitoringRound = Integer.parseInt(commander.getValue("monitoringRound", "60000"));
            route = new Route(alias); //Anadido por santiago
            EPContainer epContainer = new EPContainer(this, ep, router, route, bus, homePath + "/etc/" + propertiesFilePath, maxAttemptsReconect, microCycle, monitoringRound);
            bus.connectHandler(epContainer, route);
            
            //DEBE SER MODIFICADO PARA QUE eps RECIBA OBJETOS EPContainer Y NO OBJETOS EnvelopeProcessor
            if (nodes.put(alias, epContainer) == -1) {
                logger.error("Alias '" + alias + "' already being used!: try another alias name");
                return ("error: Alias '" + alias + "' already being used!: try another alias name");
            } else {
                ep.openConnection(homePath + "/etc/" + propertiesFilePath);
                logger.info("'" + className + "' mounted as '" + alias + "' with propertiesFilePath '" + propertiesFilePath + "' on route '" + alias + "'");
                return ("done");
            }
        } catch (java.lang.ClassNotFoundException e) {
            logger.error("Can not find the gateway " + className + " on the Filesystem", e);
            return ("Can not find the gateway " + className + " on the Filesystem");
        } catch (Exception e) {
            logger.error("Can not mount the gateway '" + className + "'", e);
            return ("Can not mount the gateway '" + className + "': " + e);
        }
        
    }
    
    public String start(String alias) throws Exception {
        EPContainer epc;
        
        if ((epc = nodes.get(alias)) == null) {
            logger.error("'" + alias + "' does not exist.");
            return ("'" + alias + "' does not exist.");
        } else {
            try {
                epc.start();
            } catch (PeerEPException e) {
                e.printStackTrace();
                logger.error("The EP could not be initiated: " + e.toString());
            }
            //Thread.yield();
            
            return ("done");
        }
    }
    
    public String stop(String alias) throws Exception {
        EPContainer epc;
        
        if ((epc = nodes.get(alias)) == null) {
            logger.error("'" + alias + "' does not exist.");
            return ("'" + alias + "' does not exist.");
        } else {
            epc.stop();
            Thread.yield();
            
            return ("done");
        }
        
    }
    
    public void unmount(String alias) {
        EPContainer epc;
        
        if ((epc = nodes.get(alias)) != null) {
            if (epc.started){
                try {
                    this.stop(alias);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            nodes.nodes.remove(alias);
            logger.error("'" + alias + "' has been unmounted.");
        }
    }
    
    public String report() {
        return ("reporte no implementado");
    }
    
    void metaStop(EPContainer c) {
        new MetaStoper(c).start();
        
    }
    
    public String status(){
        
        EPContainer container;
        String alias;
        StringBuffer sb = new StringBuffer("<");
        Map m = nodes.nodes;
        Set s = m.keySet();
        java.util.Iterator iter=s.iterator();
        while (iter.hasNext()){
            Object obj = iter.next();
            alias = (String)obj;
            container = (EPContainer)(m.get(alias));
            sb.append("Node="+alias+", route="+container.route+", started?="+container.started+", ");
        }
        sb.append(">");
        return sb.toString();
        
    }
}

class MetaStoper extends Thread{
    EPContainer container;
    /** Creates a new instance of MetaStoper */
    public MetaStoper(EPContainer c) {
        container=c;
    }
    public void run() {
        try {
            container.stop();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
